﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using System.Net;
using System.IO;
using System.Threading;

namespace GIST
{
    public class Thought : DrawableGameComponent
    {
        private const int BORDER_SIZE = 30;
        private const float SPEED = 0.1f;
        private const int FADE_IN_TIME = 100;
        private const int STAY_TRANSPARENT_TIME = 100;
        private const int FADE_OUT_TIME = 100;

        private const int GIST_SIZE = 500;
        private const int GIST_SAMPLE_COUNT = 50;

        private string text;
        public string Text
        {
            get { return text; }
            private set { text = value; }
        }

        private Texture2D cloud;

        private Texture2D texture;
        public Texture2D Texture
        {
            get { return texture; }
            private set { texture = value; }
        }

        private SpriteFont font;

        private SpriteBatch spriteBatch;
        public SpriteBatch SpriteBatch
        {
            get { return spriteBatch; }
            private set { spriteBatch = value; }
        }

        private Vector2 position;
        public Vector2 Position
        {
            get { return position; }
            set { position = value; }
        }

        private float alpha = 0;
        public float Alpha
        {
            get { return alpha; }
            set { alpha = value; }
        }

        private float scale;
        public float Scale
        {
            get { return scale; }
            set { scale = value; }
        }

        private static Random random = new Random(DateTime.Now.Millisecond);

        private static int idCounter = 0;
        private int id;

        public Thought(Game game, string thought)
            : base(game)
        {
            id = idCounter++;
            cloud = Game.Content.Load<Texture2D>("Cloud");
            Text = thought;
            SpriteBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            Texture = GetRandomTexture(Text);
            if (Texture != null)
            {
                Scale = cloud.Width / (float)Texture.Width * 0.85f;
            }
            font = Game.Content.Load<SpriteFont>("Thought");
            Show();
        }

        private int triesLeft = 3;



        private Texture2D GetRandomTexture(string searchTerm)
        {
            try
            {
                WebClient client = new WebClient();

                while (client.IsBusy)
                {
                    Thread.Sleep(10);
                }
                List<string> alternatives = new List<string>();
                int page = 0;
                int lastCount = -1;
                int currentCount = 0;
                while (alternatives.Count < GIST_SAMPLE_COUNT && alternatives.Count != lastCount)
                {
                    string searchString = "https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=" + searchTerm + "&as_filetype=jpg&userip=95.80.11.237&start=" + page;//"http://www.google.se/search?um=1&hl=sv&client=firefox-a&rls=org.mozilla:sv-SE:official&channel=np&tbm=isch&q=" + searchTerm + "&oq=" + searchTerm + "&aq=f&aqi=&gs_upl=587289l588489l0l590088l8l6l0l0l0l2l1880l7139l0.1.6-1.2.2l6l0&bav=on.2,or.r_gc.r_pw.&biw=1920&bih=1081&uss=1";
                    TextReader reader = new StreamReader(client.OpenRead(searchString));
                    string line;

                    while ((line = reader.ReadLine()) != null)
                    {
                        string[] potentialAlternatives = line.Split(new string[] { "\"url\"" }, StringSplitOptions.RemoveEmptyEntries);
                        foreach (string potentialAlternative in potentialAlternatives)
                        {
                            int startTake = potentialAlternative.LastIndexOf("http");
                            if (startTake != -1)
                            {
                                string alternative = potentialAlternative.Substring(startTake);// +".jpg";
                                alternative = alternative.Substring(0, alternative.Length - 2);
                                if (!alternatives.Contains(alternative) && !alternative.Contains("google"))
                                {
                                    alternatives.Add(alternative);
                                }
                            }
                        }
                    }
                    page += 5;

                    Console.WriteLine(searchTerm + ": " + alternatives.Count + " / " + GIST_SAMPLE_COUNT + " at page " + page);

                    // Avoid seem like a bot
                    Thread.Sleep(1000);

                    lastCount = currentCount;
                    currentCount = alternatives.Count;
                }

                if (alternatives.Count == 0)
                {
                    return null;
                }

                Texture2D tampered = new Texture2D(Game.GraphicsDevice, GIST_SIZE, GIST_SIZE);
                Color[] current = new Color[GIST_SIZE * GIST_SIZE];
                int[,] buffer = new int[3, GIST_SIZE * GIST_SIZE];
                /*tampered.GetData<Color>(current);
                for (int y = 0; y < GIST_SIZE; y++)
                {
                    for (int x = 0; x < GIST_SIZE; x++)
                    {
                        Color currentColor = current[x + y * GIST_SIZE];
                        current[x + y * GIST_SIZE] = Color.White;
                    }
                }
                tampered.SetData<Color>(current);*/
                int images =  Math.Min(GIST_SAMPLE_COUNT, alternatives.Count);
                int successStories = 0;
                for (int i = 0; i < images; i++)
                {
                    try
                    {
                        client.DownloadFile(alternatives[i], /*"Content/" + */searchTerm + "_" + i + ".jpg");
                        Texture2D original = Texture2D.FromStream(Game.GraphicsDevice, new StreamReader(/*@"Content/" + */searchTerm + "_" + i + ".jpg").BaseStream);
                        Color[] colorData = new Color[original.Width * original.Height];
                        original.GetData<Color>(colorData);

                        for (int y = 0; y < GIST_SIZE; y++)
                        {
                            for (int x = 0; x < GIST_SIZE; x++)
                            {
                                Color newColor = colorData[(int)(x / (float)GIST_SIZE * original.Width) + (int)(y / (float)GIST_SIZE * original.Height) * original.Width];
                                int gistIndex = x + y * GIST_SIZE;
                                buffer[0, gistIndex] += newColor.R;
                                buffer[1, gistIndex] += newColor.G;
                                buffer[2, gistIndex] += newColor.B;
                            }
                        }
                        successStories++;
                        Console.WriteLine(searchTerm + ": " + ((i + 1) / (float)images * 100) + " %");
                    }
                    catch (Exception ex)
                    {
                    }
                }

                Color[] newImage = new Color[GIST_SIZE * GIST_SIZE];
                for (int y = 0; y < GIST_SIZE; y++)
                {
                    for (int x = 0; x < GIST_SIZE; x++)
                    {
                        int gistIndex = x + y * GIST_SIZE;
                        newImage[gistIndex] = new Color(buffer[0, gistIndex] / (float)successStories / 255f, buffer[1, gistIndex] / (float)successStories / 255f, buffer[2, gistIndex] / (float)successStories / 255f);
                    }
                }

                tampered.SetData<Color>(newImage);
                Stream writeStream = new StreamWriter(searchTerm + ".jpg").BaseStream;
                tampered.SaveAsJpeg(writeStream, tampered.Width, tampered.Height);
                writeStream.Close();

                Console.WriteLine(searchTerm + " created!");

                return tampered;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                if (ex.Message.Contains("404") && triesLeft > 0)
                {
                    triesLeft--;
                    return GetRandomTexture(searchTerm);
                }
                else
                {
                    return null;
                }
            }
        }

        private static float NEXT_DEPTH = 1;

        private float depth;
        public float Depth
        {
            get { return depth; }
            set { depth = value; }
        }

        private bool fadeIn;
        public bool FadeIn
        {
            get { return fadeIn; }
            private set
            {
                fadeIn = value;
                counter = 0;
            }
        }
        private int counter = 0;

        public void Show()
        {
            depth = NEXT_DEPTH;
            NEXT_DEPTH -= 0.0000003f;
            if (NEXT_DEPTH <= 0)
            {
                NEXT_DEPTH = 1;
            }

            Alpha = 0;
            FadeIn = true;
            if (Texture != null)
            {
                Position = new Vector2(cloud.Width / 2f + random.Next(Game.GraphicsDevice.Viewport.Width - cloud.Width), cloud.Height + random.Next(Math.Max(1, Game.GraphicsDevice.Viewport.Height - cloud.Height - (int)(Texture.Height * Scale))));
            }
            else
            {
                Position = new Vector2(cloud.Width / 2f + random.Next(Game.GraphicsDevice.Viewport.Width - cloud.Width), cloud.Height + random.Next(Game.GraphicsDevice.Viewport.Height - cloud.Height));
            }
        }

        public override void Update(GameTime gameTime)
        {
            counter++;
            if (FadeIn)
            {
                Alpha = counter / (float)FADE_IN_TIME;
                if (counter >= FADE_IN_TIME + STAY_TRANSPARENT_TIME)
                {
                    FadeIn = false;
                }
            }
            else
            {
                Alpha = 1 - counter / (float)FADE_OUT_TIME;
            }

            Position -= new Vector2(0, SPEED);

            if (Mouse.GetState().LeftButton == ButtonState.Pressed && Vector2.Distance(Position, new Vector2(Mouse.GetState().X, Mouse.GetState().Y)) < 100)
            {
                ((Game1)Game).BannedWords.Add(Text);
                List<Thought> toRemove = new List<Thought>();
                foreach (Thought thought in Game.Components)
                {
                    if (thought.Text == Text)
                    {
                        toRemove.Add(thought);
                    }
                }
                foreach (Thought thought in toRemove)
                {
                    Game.Components.Remove(thought);
                }
            }

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            if (Texture != null)
            {
                SpriteBatch.Draw(Texture, Position, null, new Color(1, 1, 1, Alpha), 0, new Vector2(Texture.Width / 2f, 0), Scale, SpriteEffects.None, Depth);
            }

            SpriteBatch.Draw(cloud, Position, null, new Color(1, 1, 1, Alpha), 0, new Vector2(cloud.Width / 2f, cloud.Height), 1, SpriteEffects.None, Depth - 0.0000001f);

            Vector2 meassurements = font.MeasureString(Text);
            SpriteBatch.DrawString(font, Text, Position - new Vector2(0, cloud.Height * 0.45f), new Color(0, 0, 0, Alpha), 0, meassurements / 2, Math.Min(1, (cloud.Width * 0.65f) / meassurements.X), SpriteEffects.None, Depth - 0.0000002f);

            base.Draw(gameTime);
        }
    }
}
